package cascade
{
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.clearInterval;
	import flash.utils.getTimer;
	import flash.utils.setInterval;
	
	import mx.events.PropertyChangeEvent;
	import mx.events.PropertyChangeEventKind;
	
	public class CascadeBusy extends EventDispatcher
	{
		protected var busies:Dictionary = new Dictionary();
		protected var busyCheckInterval:uint;
		protected var _isBusy:Boolean = false;
		
		/**
		 * Adds a busy token to the stack to keep track of loading operations etc.
		 * This allows a progress bar or some other item to keep track of when the
		 * application is busy doing something.
		 * 
		 * Requires the object which is busy to be passed in so that when it the
		 * operation is finished it may relinquish the busy ticket. A timeout
		 * is to be included so the system will not stay busy forever if a call
		 * never completes. This is in seconds.
		 */
		public function addBusy(target:Object, timeout:Number = 10):void
		{
			busies[target] = getTimer() + timeout*1000;
			
			busyCheckInterval = setInterval(checkBusies, 100);
			setBusy(true);
		}
		
		/**
		 * Removes a busy ticket from the stack. The object which initiated the call
		 * should relinquish the busy ticket by passing itself in.
		 */
		public function removeBusy(target:Object):void
		{
			delete busies[target];
			checkBusies();
		}
		
		[Bindable("propertyChange")]
		public function get isBusy():Boolean
		{
			return _isBusy;
		}
		
		protected function setBusy(value:Boolean):void
		{
			if (value == _isBusy)
				return;
			
			_isBusy = value;
			dispatchEvent(new PropertyChangeEvent(
					PropertyChangeEvent.PROPERTY_CHANGE,
					false,
					false,
					PropertyChangeEventKind.UPDATE,
					"isBusy",
					!value,
					value,
					this));
		}
		
		
		protected function checkBusies():void
		{
			var time:uint = getTimer();
			var busiesLeft:Boolean = false;
			
			for (var target:Object in busies)
			{
				if (busies[target] >= time)
					delete busies[target];
				else
					busiesLeft = true;
			}
			
			if (!busiesLeft)
			{
				setBusy(false);
				clearInterval(busyCheckInterval);
				busyCheckInterval = 0;
			}
		}
	}
}